Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: OpenDoc Programmer's Guide / Part 2 - Programming
Chapter 5 - User Events


Focus Transfer

Part activation is the process of making a part and frame ready for editing. As noted previously, activation typically occurs when the user clicks the mouse button when the pointer is within a frame that is not currently active, but it also happens when a window opens, when a window is activated, and as a result of drag-and-drop operations.

In the OpenDoc model of part activation, part editors use the concept of focus to activate and deactivate themselves (rather than being activated and deactivated by OpenDoc) and to arbitrate the transfer of several types of shared resources among themselves.

Focus Types

A part makes itself the recipient of a certain type of user event or other action by obtaining the focus for it. A focus is a designation of ownership of a given shared resource, feature, or event type; for example, the frame that owns the keystroke focus receives all keystroke events until it passes ownership of the keystroke focus to another frame.

Focus types are defined as ISO strings. Table 5-4 lists the standard set defined by OpenDoc.
Table 5-4 Focus types
ConstantISO stringDescription
kODKeyFocus"Key"Keystroke events are sent to the frame with this focus.
kODMenuFocus"Menu"Menu events are sent first to the frame with this focus.
kODSelectionFocus"Selection"Shift-click and Command-click mouse events are sent to the frame with this focus. OpenDoc draws the active frame border around all facets of this frame.
kODModalFocus"Modal"The frame that owns this focus is notifying other frames that it is the only current modal frame.
kODScrollingFocus"Scrolling"Scrolling-specific keystroke events (such as Page Up and Page Down) are sent to the frame with this focus.
kODClipboardFocus"Clipboard"The frame that owns this focus has access to the clipboard.
kODMouseFocus"Mouse"The frame that owns this focus receives all mouse-within events whenever the pointer moves, and all mouse-down and mouse-up events, regardless of which facet the pointer is within.

To obtain event foci, part editors request them by name from the arbitrator. (You obtain access to the arbitrator by calling the GetArbitrator method of the session object.)

You need to convert focus names into tokens before using them in any method calls. You call the Tokenize method of the session object to convert ISO strings to tokens.

Foci may be manipulated singly or in groups called focus sets. A focus set is an OpenDoc object (of class ODFocusSet) listing a group of foci that a part editor wants to obtain or release as a group.

Foci are owned by frames. In general, mouse events anywhere within the content area of an OpenDoc window always go to the most deeply embedded frame that encloses the click point. However, Shift-click and Command-click events, regardless of their location, are sent to the frame with the selection focus to allow for extending selections.

OpenDoc does not require that the same frame own the selection focus, keystroke focus, and menu focus, although this is most often the case. Also, OpenDoc does not require that the selection focus be in an active window, although this is usually the case, at least on the Mac OS platform.

In most cases, when a frame is activated the part editor for that frame requests the selection focus, keystroke focus, and menu focus. A frame with scroll bars might also request the scrolling focus. Your part editor might create a focus set ahead of time, perhaps during part initialization, that includes the tokenized names of the foci that your part expects to request when it becomes active. You use the arbitrator's CreateFocusSet method to create the focus set.

A simple part, such as a small text-entry field in a dialog box, might request only the selection focus and keystroke focus on receiving a mouse-up event within its frame area. An even simpler part, such as a button, might not even request the selection focus. It might simply track the mouse until the button is released, and then run a script, never having changed the menu bar, put up palettes or rulers, or become active.

Your part editor can define additional focus types as needed. You can define other kinds of focus, perhaps to handle other kinds of user events (such as input from new kinds of devices). To create a new kind of focus, you need to create a new kind of focus module, the OpenDoc object that the arbitrator uses to determine focus ownership. Chapter 10, "Extending OpenDoc," describes how to use focus modules to extend OpenDoc's focus management.

Foci may be exclusive or nonexclusive. All of the standard foci defined by OpenDoc are exclusive, meaning that only one frame at a time can own a focus. But if you create a new kind of focus, you can make it nonexclusive, meaning that several frames could share ownership of it.

Arbitrating Focus Transfers

This section discusses how to request or relinquish foci to activate or deactivate your frames.

Requesting Foci

A part can request, for one of its frames, ownership of a single focus or a set of foci. You request a focus by calling the arbitrator's RequestFocus method; you request a focus set by calling the arbitrator's RequestFocusSet method. If the request succeeds, your part's frame obtains the focus or focus set.

The arbitrator's RequestFocus and RequestFocusSet methods perform a two-stage transaction in transferring a focus or focus set:

  1. The arbitrator first asks the current owning frame of each focus if it is willing to relinquish the focus, by calling the BeginRelinquishFocus method of the frame's part.
  2. If any owner of the focus is unwilling to relinquish it, the arbitrator cancels the request by calling each part's AbortRelinquishFocus method. In this case, RequestFocus or RequestFocusSet returns false.
  3. If all focus owners are willing to relinquish, the arbitrator calls each part's CommitRelinquishFocus method. In this case, RequestFocus or RequestFocusSet returns true.

Relinquishing Foci

A part can relinquish foci either on request or when a change to its state (such as the closure of its frame or a completion of a method) warrants it. An active part might unilaterally relinquish certain foci (such as the clipboard focus) as soon as it is finished handling an event, but it might not relinquish other foci (such as the selection focus) until another part asks for them. Nevertheless, most parts willingly relinquish the common foci when asked.

Relinquishing foci on request is a two-step process, because multiple foci requested as a focus set must all be provided to the requestor simultaneously; if one is not available, none need be relinquished. Your part editor participates in the process through calls to its BeginRelinquishFocus, CommitRelinquishFocus, and AbortRelinquishFocus methods.

  1. In your BeginRelinquishFocus method, you need do nothing other than return kODTrue or kODFalse, basing your decision on the type of focus and the identities of the frames (current and proposed focus owners) passed to you. In most cases you can simply return kODTrue, unless your part is displaying a dialog box and another part is requesting the modal focus. In that case, because you do not want to yield the modal focus until your dialog box window closes, you return kODFalse. See "Acquiring and Relinquishing the Modal Focus" for more information.
  2. Your part's CommitRelinquishFocus method verifies that you have actually relinquished the focus type you responded to in BeginRelinquishFocus. The method should take appropriate action, such as removing menus or palettes, disabling menu items, removing highlighting, and performing whatever other tasks are part of losing that type of focus. Remember that the focus may possibly be moving from one frame to another of your part, so the exact actions can vary.
  3. If, after your part responds with kODTrue to BeginRelinquishFocus, the focus is actually not transferred from your frame, OpenDoc calls your part's AbortRelinquishFocus method. If your part has done anything more than return the Boolean result in BeginRelinquishFocus, it can undo those effects in the AbortRelinquishFocus method.
  4. If your part is one of several focus owners called to relinquish the foci of a focus set, and if you return kODFalse to BeginRelinquishFocus, your CommitRelinquishFocus method is not called (because you chose not to give up the focus). However, your AbortRelinquishFocus method is still called (because all owners of a focus set are notified if any one refuses to relinquish the focus).

Your part does not relinquish its focus on request only. For example, in your part's DisplayFrameClosed and DisplayFrameRemoved methods, you should include a call to the arbitrator's RelinquishFocus or RelinquishFocusSet method to unilaterally relinquish any foci owned by the frame that you are closing. When your part closes, its ReleaseAll method should likewise relinquish all of its foci. When your part finishes displaying a modal dialog box, it should relinquish or transfer the modal focus; when your part finishes accessing the clipboard, it should relinquish the clipboard focus.

Transferring Focus Without Negotiation

There are some situations in which the normal process of requesting and relinquishing foci is not used. Another piece of software interrupts your part's execution, and your part loses a focus without being given a chance to relinquish it, or gains focus without having asked for it. To handle those situations, your part editor must implement versions of the methods FocusAcquired and FocusLost. The arbitrator calls these methods when your part has just acquired, or just lost, a specified focus without having negotiated the transaction.

For example, a containing part, to support keyboard navigation, might call FocusAcquired in turn on each of its embedded parts as the user makes successive keystrokes. Or, if a custom input device with its own focus type were in use and then became detached, the part using the device might receive a call to its FocusLost method.

These are the interfaces to FocusAcquired and FocusLost:

void FocusAcquired(in ODTypeToken focus,
                   in ODFrame ownerFrame);
void FocusLost(in ODTypeToken focus,
               in ODFrame ownerFrame);
Your FocusAcquired and FocusLost methods should perform any actions that your part editor deems appropriate in response to having just acquired or lost a focus.

The arbitrator's methods TransferFocus and TransferFocusSet allow you to initiate a transfer of focus ownership without negotiation. A part can use these calls to transfer focus among parts and frames that it controls directly. For example, in a modal dialog box consisting of several parts, these methods can be used to transfer a focus from the outer part (the dialog box) directly to an inner part (such as a text field) and back.

When focus is transferred with TransferFocus or TransferFocusSet, the arbitrator generally calls the FocusAcquired method of the new frame's part and the FocusLost method of the previous frame's part. However, when the frame performing the transfer (the frame representing the part that calls TransferFocus) is the frame receiving or losing the focus, its FocusAcquired or FocusLost method is not called.

Calling your own FocusAcquired and FocusLost
It might seem natural to call your own FocusAcquired method when your request for foci succeeds, or to call your own FocusLost method from your own CommitRelinquishFocus method. A better practice, however, is to have related methods call a shared private method, so that you maintain a clear separation between public and private interfaces.

Recording Focus Transfers

Different frames may need different sets of foci when activated. Selection focus, keystroke focus, and menu focus are commonly needed together. However, a frame with scroll bars might also need the scrolling focus, and a frame for a modeless dialog box might not want the selection focus.

OpenDoc does not save or restore focus assignments. Therefore, during deactivation of windows and frames, and during closing of windows, you can record the state of focus ownership so that you can restore it at a later activation or reopening. Your display frame's part info is an appropriate place to keep that information. Your part's initialization method might create a focus set with those foci, to use whenever your display frames become active.

On Frame Activation

When a previously inactive frame in a window becomes active, the part editors involved should--besides negotiating the focus transfer--record the gain or loss of selection focus for the respective frames. If you maintain that information, your activation and deactivation routines can check the state and exit quickly if no change in the active state is needed.

On Window Activation

As mentioned in "Activate Events", all parts displayed in a window receive an activate event when the window becomes active, and a deactivate event when the window becomes inactive.

When an active facet of a frame of your part becomes inactive through window deactivation, your part's HandleEvent method can--upon receiving the deactivate event--store a flag in the facet's part info field to note that the facet was active before window deactivation. Your part then can also maintain, as a background selection, any selection it had been displaying.

Conversely, when a facet of a frame of your part receives an activate event because of window activation, your part's HandleEvent method can examine the state of the flag in the part info field to determine whether it was the active part when the window became inactive. If so, it should request the selection focus, reset the flag, and convert any background selection it may have maintained into a foreground selection.

On Closing and Reopening Documents

Normally, the root part of a newly opened window should activate itself as a matter of course. However, if an embedded part had the selection focus when the window closed, the root part can--if it chooses to--allow the embedded part to recapture that focus when the window reopens.

When the state of a window is saved in a document and the document is subsequently reopened, the root part recreates the window. If you want to restore the selection-focus state of your part (plus perhaps the selection itself), you can save the selection and the state of the selection-focus flag in your frame's part info data when the window is closed, and restore them when the window is opened (when your part's DisplayFrameConnected method is called).

If your part is the root part in this situation, you can either grant the embedded part's request for the selection focus at this time, or you can acquire the selection focus yourself, when your own DisplayFrameConnected method is called. (The root part is called last.)


Previous Book Contents Book Index Next

© Apple Computer, Inc.
16 JUL 1996




Navigation graphic, see text links

Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help